/*
 * Copyright (c) 2018 Hitevision
 */

package tw.com.hitevision.whiteboard.android.storage;

import android.content.Context;
import android.os.Environment;
import android.os.IBinder;
import android.os.storage.StorageManager;
import android.util.Log;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import androidx.annotation.Nullable;
import tw.com.hitevision.whiteboard.android.R;

/**
 * This class helps with using external storage.
 */
public class StorageHelper {
    private static final String TAG = StorageHelper.class.getSimpleName();

    private static StorageHelper instance;

    public static StorageHelper getInstance(Context context) {
        if (instance == null) {
            instance = new StorageHelper(context.getApplicationContext());
        }
        return instance;
    }

    private final StorageManager storageManager;
    private Method getVolumePaths;
    private Method getVolumeState;

    private Object iMountService; // IMountService instance
    private Method getVolumeLabel;

    private final String genericLabel;

    private Method getVolumeList;

    private StorageHelper(Context context) {
        genericLabel = context.getString(R.string.usb_generic);

        storageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);

        try {
            getVolumePaths = StorageManager.class.getDeclaredMethod("getVolumePaths");
            getVolumePaths.setAccessible(true);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        try {
            getVolumeState = StorageManager.class.getDeclaredMethod("getVolumeState", String.class);
            getVolumeState.setAccessible(true);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        try {
            final Class<?> serviceManagerClass = Class.forName("android.os.ServiceManager");
            final Method getService = serviceManagerClass.getMethod("getService", String.class);
            final IBinder mountService = (IBinder) getService.invoke(null, "mount");
            if (mountService != null) {
                final Class<?> stubClass = Class.forName("android.os.storage.IMountService$Stub");
                final Method asInterface = stubClass.getMethod("asInterface", IBinder.class);
                iMountService = asInterface.invoke(null, mountService);
            }

            final Class iMountServiceClass = Class.forName("android.os.storage.IMountService");
            getVolumeLabel = iMountServiceClass.getMethod("getVolumeLabel", String.class);
        } catch (Exception e) {
            e.printStackTrace();
        }

        try {
            getVolumeState = StorageManager.class.getDeclaredMethod("getVolumeState", String.class);
            getVolumeState.setAccessible(true);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        try {
            Class<?>[] paramClasses = {};
            getVolumeList = StorageManager.class.getDeclaredMethod("getVolumeList", paramClasses);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

    }

    public List<Volume> getVolumes() {
        final List<Volume> volumes = new ArrayList<>();

//        if (storageManager != null && getVolumePaths != null && getVolumeState != null) {
//            String primaryStoragePath = Environment.getExternalStorageDirectory().getPath();
//            try {
//                final String[] paths = (String[]) getVolumePaths.invoke(storageManager);
//                if (paths != null) {
//                    for (String path : paths) {
//                        log("- " + path);
//                        if (path != null && !path.equals(primaryStoragePath)) {
//                            final String state = (String) getVolumeState.invoke(storageManager, path);
//                            if (Environment.MEDIA_MOUNTED.equals(state)) {
//                                String label = getUsbDeviceLabel(path);
//                                if (label == null) {
//                                    label = genericLabel;
//                                }
//                                volumes.add(new Volume(label.trim(), path));
//                            }
//                        }
//                    }
//                }
//            } catch (Exception e) {
//                e.printStackTrace();
//            }
//        }

        if (storageManager != null && getVolumeList != null && getVolumeState != null) {
            String primaryStoragePath = Environment.getExternalStorageDirectory().getPath();
            try {
                final Object[] usbList = (Object[]) getVolumeList.invoke(storageManager);
                if (usbList != null) {
                    for (Object usb : usbList) {
                        String path = (String) usb.getClass().getMethod("getPath").invoke(usb);
                        if (path != null && !path.equals(primaryStoragePath)) {
                            final String state = (String) getVolumeState.invoke(storageManager, path);
                            if (Environment.MEDIA_MOUNTED.equals(state)) {
                                String label = (String) usb.getClass().getMethod("getUserLabel").invoke(usb);
                                if (label == null) {
                                    label = genericLabel;
                                }
                                volumes.add(new Volume(label.trim(), path));
                            }
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        return volumes;
    }

    @Nullable
    private String getUsbDeviceLabel(String mountPath) {
        if (iMountService != null && getVolumeLabel != null) {
            try {
                return (String) getVolumeLabel.invoke(iMountService, mountPath);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    private static void log(String msg) {
        Log.d(TAG, msg);
    }
}
